#lang scribble/base

@(require "shared.rkt" scribble/eval)

@title{@;{Choosing the Right Construct}选择正确的构造}

@;{Racket provides a range of constructs for the same or similar purposes.
Although the Racket designers don't think that there is one right way for
everything, we prefer certain constructs in certain situations for consistency
and readability.}
Racket为相同或相似的用途提供了一系列的构造，虽然Racket设计者认为没有一种方法适合所有的东西，但为了一致性和可读性，我们更喜欢在某些情况下使用某些构造。

@; -----------------------------------------------------------------------------
@section{@;{Comments}注释}

@;{Following Lisp and Scheme tradition, we use a single semicolon for in-line
comments (to the end of a line) and two semicolons for comments that start
a line. @margin-note*{This request does not contradict the programs in this
document. They use two semicolons for full-line comments in source but
scribble renders only one.} Think of the second semicolon as making an
emphatic point.}
遵循Lisp和Scheme的传统，我们对行内注释使用一个分号（直到行尾），对开始行的注释使用两个分号。@margin-note*{此要求与此文档中的程序不矛盾。源代码中的整行注释使用两个分号，而scribble只呈现一个。}将第二个分号看作是一个强调点。

@;{Seasoned Schemers, not necessarily Racketeers, also use triple and
quadruple semicolons. This is considered a courtesy to distinguish file
headers from section headers.}
经验丰富的Scheme程序员，不一定是Racket程序员，也会使用三个和四个分号。区分文件头和节头被认为是一种礼貌。

@;{In addition to @litchar{;}, we have two other mechanisms for commenting code:}
除了@litchar{;}，我们还有另外两种注释代码的机制：
 @;{@litchar{#|}...@litchar{|#} for blocks and @litchar{#;} to comment out an expression.}
@litchar{#|}……@litchar{|#}用于块，而@litchar{#;}用于注释表达式
 @;{@defterm{Block comments} are for those rare cases when an entire block of
 definitions and/or expressions must be commented out at once.}
@defterm{块注释（block comments）}适用于必须同时注释掉整个定义和（或）表达式块的罕见情况。
 @;{@defterm{Expression comments}---@litchar{#;}---apply to the following
 S-expression.  This makes them a useful tool for debugging.  They can even
 be composed in interesting ways with other comments, for example, @litchar{#;#;}
 will comment two expressions, and a line with just @litchar{;#;} gives you a
 single-character ``toggle'' for the expression that starts on the next
 line.  But on the flip side, many tools don't process them
 properly---treating them instead as a @litchar{#} followed by a commented line.}
@defterm{表达式注释（expression comments）}——@litchar{#;}——应用于以下S表达式。这使它们成为调试的有用工具。它们甚至可以以有趣的方式与其他注释组合在一起，例如，@litchar{#;#;}将对两个表达式进行注释，而只有@litchar{;#;}的一行将为从下一行开始的表达式提供一个字符“切换（toggle）”。但另一方面，许多工具并没有正确地处理它们——而是将它们视为一个@litchar{#}后跟一个注释行。
                                           
 @;{For example, in DrRacket S-expression comments are ignored when it comes
 to syntax coloring, which makes it easy to miss them. In Emacs, the
 commented text is colored like a comment and treated as text, which makes
 it difficult to edit as code.  The bottom line here is that @litchar{#;}
 comments are useful for debugging, but try to avoid leaving them in
 committed code.  If you really want to use @litchar{#;}, clarify their use with
 a line comment (@litchar{;}).}
例如，在DrRacket中，当涉及到语法着色时，S-expression注释被忽略，这使得很容易忽略它们。在Emacs中，注释文本的颜色像注释一样，并被视为文本，这使得它很难作为代码进行编辑。这里的底线是@litchar{#;}注释对于调试很有用，但是尽量避免将它们留在提交的代码中。如果您真的想使用@litchar{#;}，请用一个行注释（@litchar{;}）澄清它们的用法。

@; -----------------------------------------------------------------------------
@section{@;{Definitions}定义}

@;{Racket comes with quite a few definitional constructs, including
@scheme[let], @scheme[let*], @scheme[letrec], and @scheme[define]. Except
for the last one, definitional constructs increase the indentation level.
Therefore, favor @scheme[define] when feasible.}
Racket有很多定义构造，包括@scheme[let]、@scheme[let*]、@scheme[letrec]和@scheme[define]。除了最后一个，定义构造增加了缩进级别。
因此，在可行的情况下，赞成使用@scheme[define]。

@compare[
@racketmod0[#:file
@tt{@;{good}好的}
racket

(define (swap x y)
  (define t (unbox x))
  (set-box! x (unbox y))
  (set-box! y t))
]
@; -----------------------------------------------------------------------------
@racketmod0[#:file
@tt{@;{bad}差的}
racket

(define (swap x y)
  (let ([t (unbox x)])
    (set-box! x (unbox y))
    (set-box! y t)))
]
]

@;{@bold{Warning} A @racket[let*] binding block is not easily replaced with a
series of @racket[define]s because the former has @emph{sequential} scope
and the latter has @emph{mutually recursive} scope.}
@bold{警告} @racket[let*]绑定块不容易替换为一系列的@racket[define]，因为前者具有@emph{序列的}作用域，后者具有@emph{相互递归}作用域。

@compare[
@racketmod0[#:file
@tt{@;{works}能运行}
racket
(define (print-two f)
  (let* ([_ (print (first f))]
	 [f (rest f)]
	 [_ (print (first f))]
	 [f (rest f)])
    (code:comment "IN")
    f))
]
@; -----------------------------------------------------------------------------
@racketmod0[#:file

@tt{@;{does @bold{not}}@bold{不}能运行}
racket

(define (print-two f)
   (print (first f))
   (define f (rest f))
   (print (first f))
   (define f (rest f))
   (code:comment "IN")
   f)
]
]

@; -----------------------------------------------------------------------------
@section{@;{Conditionals}条件表达式}

@;{Like definitional constructs, conditionals come in many flavors,
too. Because @scheme[cond] and its relatives (@scheme[case],
@scheme[match], etc) now allow local uses of @scheme[define], you should
prefer them over @scheme[if].}
和定义构造一样，条件句也有很多种形式。因为@scheme[cond]及其同类（@scheme[case]、@scheme[match]等）现在允许在局部使用@scheme[define]，所以您应该比@scheme[if]更倾向于它们。

@compare[
@racketmod0[#:file
@tt{@;{good}好的}
racket

(cond
  [(empty? l) #false]
  [else
   (define f (first l))
   (define r (rest l))
   (if (discounted? f)
       (rate f)
       (curved (g r)))])
]
@racketmod0[#:file
@tt{@;{bad}差的}
racket

(if (empty? l)
    #false
    (let ([f (first l)]
	  [r (rest l)])
      (if (discounted? f)
          (rate f)
          (curved (g r)))))
]
]

@;{Also, use @racket[cond] instead of @racket[if] to eliminate explicit
 @racket[begin].}
另外，使用@racket[cond]而不是@racket[if]来消除显式的@racket[begin]。

@;{The above ``good'' example would be even better with @racket[match]. In
 general, use @racket[match] to destructure complex pieces of data.}
上面的“好”例子使用@racket[match]会更好。一般来说，使用@racket[match]对复杂的数据片段进行解构。

@;{You should also favor @scheme[cond] (and its relatives) over @scheme[if] to
 match the shape of the data definition. In particular, the above examples
 could be formulated with @racket[and] and @racket[or] but doing so would
 not bring across the recursion as nicely.}
您还应该支持@scheme[cond]而不是@scheme[if]，以匹配数据定义的情况。特别是，上面的例子可以用@racket[and]和@racket[or]来表示，但是这样做并不能很好地传递递归。

@; -----------------------------------------------------------------------------
@section{@;{Expressions}表达}

@;{Don't nest expressions too deeply. Instead name intermediate results. With
well-chosen names your expression becomes easy to read.}
不要把表达嵌套得太深。而是命名中间结果。有了精心挑选的名字，你的表达变得容易阅读。

@compare[
@racketmod0[#:file
@tt{@;{good}好的}
racket
(define (next-month date)
  (define day (first date))
  (define month (second date))
  (if (= month 12)
      `(,(+ day 1) 1)
      `(,day ,(+ month 1))))
]
@; -----------------------------------------------------------------------------
@racketmod0[#:file
@tt{@;{bad}差的}
racket
(define (next-month d)
  (if (= (cadr d) 12)
      `(,(+ (car d) 1)
	1
	,(caddr d))
      `(,(car d)
	,(+ (cadr d) 1))))
]
]
 @;{Clearly ``too deeply'' is subjective. On occasion it also isn't the
 nesting that makes the expression unreadable but the sheer number of
 subexpressions. Consider using local definitions for this case, too.}
显然，“太深”是主观的。有时，使表达式不可读的并不是嵌套，而是子表达式的绝对数量。考虑在这种情况下也使用局部定义。

@; -----------------------------------------------------------------------------
@section{@;{Structs vs Lists}结构与列表}

@;{Use @racket[struct]s when you represent a combination of a small and fixed
number of values.  For fixed length (long) lists, add a comment or even a
contract that states the constraints.}
当你表示一个小的和固定数量的值的组合时，使用@racket[struct]。对于固定长度（长）列表，请添加注释，甚至添加声明约束的合约。

@;{If a function returns several results via @racket[values], consider using
@racket[struct]s or lists when you are dealing with four or more values.}
如果函数通过@racket[values]返回多个结果，那么在处理四个或更多值时，请考虑使用@racket[struct]或列表。

@; -----------------------------------------------------------------------------
@section{@;{Lambda vs Define}Lambda与Define}

@;{While nobody denies that @racket[lambda] is cute, @racket[define]d
functions have names that tell you what they compute and that help
accelerate reading.}
虽然没有人否认@racket[lambda]很可爱，但@racket[define]构造的函数的名称可以告诉您它们的计算结果，并有助于加快阅读速度。

@compare[
@racketmod0[#:file
@tt{@;{good}好的}
racket

(define (process f)
  (define (complex-step x)
    ... 10 lines ...)
  (map complex-step
       (to-list f)))
]
@; -----------------------------------------------------------------------------
@racketmod0[#:file
@tt{@;{bad}差的}
racket

(define (process f)
  (map (lambda (x)
	 ... 10 lines ...)
       (to-list f)))
]
]

@;{Even a curried function does not need @racket[lambda].}
即使是柯理化函数也不需要@racket[lambda]。
@compare[
@racketmod0[#:file
@tt{@;{good}好的}
racket

(define ((cut fx-image) image2)
  ...)
]
@; -----------------------------------------------------------------------------
@racketmod0[#:file
@tt{@;{acceptable}可接受的}
racket

(define (cut fx-image)
  (lambda (image2)
    ...))
]
]
 @;{The left side signals currying in the very first line of the function,
 while the reader must read two lines for the version on the right side.}
左边的标志出现在函数的第一行，而读取器必须读取右边版本的两行。

@;{Of course, many constructs (call-with ..) or higher-order functions
(filter) are made for short @racket[lambda]; don't hesitate to use
@racket[lambda] for such cases.}
当然，许多构造(call-with ..)或高阶函数(filter)都是为短@racket[lambda]创建的；在这种情况下，不要犹豫使用@racket[lambda]。


@; -----------------------------------------------------------------------------
@section{@;{Identity Functions}特征函数}

@;{The identity function is @racket[values]:}
特征函数是@racket[values]：

 @examples[
 (map values '(a b c))
 (values 1 2 3)
 ]

@; -----------------------------------------------------------------------------
@section{@;{Traversals}遍历}

@;{With the availability of @racket[for/fold], @racket[for/list],
 @racket[for/vector], and friends, programming with @racket[for] loops
 has become just as functional as programming with @racket[map] and
 @racket[foldr]. With @racket[for*] loops, filter, and termination clauses
 in the iteration specification, these loops are also far more concise than
 explicit traversal combinators. And with @racket[for] loops, you can
 decouple the traversal from lists.}
有了@racket[for/fold]，@racket[for/list]，
@racket[for/vector]和朋友们，使用@racket[for]循环编程已经变得和使用@racket[map]和@racket[foldr]编程一样实用。使用迭代规范中的@racket[for*]循环、过滤器和终止子句，这些循环也比显式遍历组合器简洁得多。使用@racket[for]循环，可以将遍历与列表分离。

@margin-note*{@;{See also @racket[for/sum] and @racket[for/product] in Racket.}另请参见Racket中的@racket[for/sum]和@racket[for/product]。}
@compare[
@;%
@(begin
#reader scribble/comment-reader
[racketmod0 #:file
@tt{@;{good}好的}
racket

;; [Sequence X] -> Number
(define (sum-up s)
  (for/fold ((sum 0)) ((x s))
    (+ sum x)))

;; examples:
(sum-up '(1 2 3))
(sum-up #(1 2 3))
(sum-up
  (open-input-string
    "1 2 3"))
])
@; -----------------------------------------------------------------------------
@;%
@(begin
#reader scribble/comment-reader
[racketmod0 #:file
@tt{@;{bad}差的}
racket

;; [Listof X] -> Number
(define (sum-up alist)
  (foldr (lambda (x sum)
            (+ sum x))
          0
          alist))

;; example:
(sum-up '(1 2 3))
])
]
 @;{In this example, the @racket[for] loop on the left comes with two
 advantages. First, a reader doesn't need to absorb an intermediate
 @racket[lambda]. Second, the @racket[for] loop naturally generalizes to
 other kinds of sequences. Naturally, the trade-off here is a loss of
 efficiency; using @racket[in-list] to restrict the @tt{good} example to
 the same range of data as the @tt{bad} one speeds up the former.}
在这个例子中，左边的@racket[for]循环有两个优点。首先，读者不需要理解中间的@racket[lambda]。其次，@racket[for]循环自然地推广到其他类型的序列。当然，这里的取舍是效率的损失；使用@racket[in list]将@tt{好的}示例限制在与@tt{差的}相同的数据范围内可以加快前者的速度。

 @;{@bold{Note}: @racket[for] traversals of user-defined sequences tend to be
 slow. If performance matters in these cases, you may wish to fall back on
 your own traversal functions.}
@bold{注意}：@racket[for]遍历用户定义的序列往往很慢。如果在这些情况下性能很重要，您可能希望依靠自己的遍历函数。

@; -----------------------------------------------------------------------------
@section{@;{Functions vs Macros}函数与宏}

@;{Define functions when possible, Or, do not introduce macros when functions
will do.}
在可能的情况下定义函数，或者，在函数执行时不要引入宏。

@compare[
@racketmod0[#:file
@tt{@;{good}好的}
racket
...
;; Message -> String
(define (name msg)
  (first (second msg)))
]
@; -----------------------------------------------------------------------------
@(begin
#reader scribble/comment-reader
[racketmod0 #:file
@tt{@;{bad}差的}
racket
...
;; Message -> String
(define-syntax-rule (name msg)
  (first (second msg)))
]
)
]
 @;{A function is immediately useful in a higher-order context. For a macro,
 achieving the same goal takes a lot more work.}
函数在高阶上下文中立即有用。对于宏来说，实现相同的目标需要做更多的工作。


@; -----------------------------------------------------------------------------
@section{@;{Exceptions}异常}

@;{When you handle exceptions, specify the exception as precisely as
possible.}
处理异常时，请尽可能精确地指定异常。

@compare[
@racketmod0[#:file
@tt{@;{good}好的}
racket
...
(code:comment #, @t{FN [X -> Y] FN -> Void})
(define (convert in f out)
  (with-handlers
      ((exn:fail:read? X))
    (with-output-to out
      (writer f))))

(code:comment #, @t{@;{may raise exn:fail:read}可能引发exn:fail:read异常})
(define ((writer f))
 (with-input-from in
   (reader f)))

(code:comment #, @t{@;{may raise exn:fail:read}可能引发exn:fail:read异常})
(define ((reader f))
 ... f ...)
]
@; -----------------------------------------------------------------------------
@racketmod0[#:file
@tt{@;{bad}差的}
racket
...
(code:comment #, @t{FN [X -> Y] FN -> Void})
(define (convert in f out)
  (with-handlers
      (((code:hilite (lambda _ #t)) X))
    (with-output-to out
      (writer f))))

(code:comment #, @t{@;{may raise exn:fail:read}可能引发exn:fail:read})
(define ((writer f))
 (with-input-from in
   (reader f)))

(code:comment #, @t{@;{may raise exn:fail:read}可能引发exn:fail:read})
(define ((reader f))
 ... f ...)
]
]
 @;{Using @racket[(lambda _ #t)] as an exception predicate suggests to the
 reader that you wish to catch every possible exception, including failure
 and break exceptions. Worse, the reader may think that you didn't remotely
 consider what exceptions you @emph{should} be catching.}
使用 @racket[(lambda _ #t)]作为异常判断向读者建议，您希望捕获每个可能的异常，包括失败和中断异常。更糟糕的是，读取器可能会认为，你根本没有考虑到@emph{应该}捕捉哪些异常。

@;{It is equally bad to use @racket[exn?] as the exception predicate  even if
 you mean to catch all kinds of failures. Doing so catches break
 exceptions, too. To catch all failures, use @racket[exn:fail?] as shown on
 the left:}
使用@racket[exn?]作为异常判断，即使您打算捕获所有类型的失败。这样做也会捕获中断异常。要捕获所有失败，请使用@racket[exn:fail?]如左图所示：

@compare[
@racketmod0[#:file
@tt{@;{good}好的}
racket
...
(code:comment #, @t{FN [X -> Y] FN -> Void})
(define (convert in f out)
  (with-handlers
      ((exn:fail? X))
    (with-output-to out
      (writer f))))

(code:comment #, @t{@;{may raise exn:fail:read}可能会引发exn:fail:read异常})
(define ((writer f))
 (with-input-from in
   (reader f)))

(code:comment #, @t{@;{may raise exn:fail:read}可能会引发exn:fail:read异常})
(define ((reader f))
 ... f ...)
]
@racketmod0[#:file
@tt{@;{bad}差的}
racket
...
(code:comment #, @t{FN [X -> Y] FN -> Void})
(define (convert in f out)
  (with-handlers
      (((code:hilite exn?) X))
    (with-output-to out
      (writer f))))

(code:comment #, @t{@;{may raise exn:fail:read}可能会引发exn:fail:read异常})
(define ((writer f))
 (with-input-from in
   (reader f)))

(code:comment #, @t{@;{may raise exn:fail:read}可能会引发exn:fail:read异常})
(define ((reader f))
 ... f ...)
]
]

@;{Finally, a handler for a @racket[exn:fail?] clause should never
 succeed for all possible failures because it silences all kinds of
 exceptions that you probably want to see:}
最后，一个@racket[exn:fail?]子句不应成功处理所有可能的失败，因为它会使您可能希望看到的所有类型的异常都静音：
@codebox[
@racketmod0[#:file
@tt{@;{bad}差的}
racket
...
(code:comment #, @t{FN [X -> Y] FN -> Void})
(define (convert in f out)
  (with-handlers ((exn:fail? handler))
    (with-output-to out
      (writer f))))

(code:comment #, @t{Exn -> Void})
(define (handler e)
  (cond
    [(exn:fail:read? e)
     (displayln "drracket is special")]
    [else (void)]))

(code:comment #, @t{@;{may raise exn:fail:read}可能会引发exn:fail:read异常})
(define ((writer f))
 (with-input-from in
   (reader f)))

(code:comment #, @t{@;{may raise exn:fail:read}可能会引发exn:fail:read异常})
(define ((reader f))
 ... f ...)
]
]
 @;{If you wish to deal with several different kind of failures, say
 @racket[exn:fail:read?] and @racket[exn:fail:network?], use distinct
 clauses in @racket[with-handlers] to do so and distribute the branches of
 your conditional over these clauses.}
如果你想处理几种不同的失败，比如@racket[exn:fail:read?]以及@racket[exn:fail:network?]，在@racket[with-handlers]中使用确切的子句来执行此操作，并在这些子句上分布条件的分支。

@; -----------------------------------------------------------------------------
@section{@;{Parameters}参数}

@;{If you need to set a parameter, use @racket[parameterize]:}
如果需要设置参数，请使用@racket[parameterize]：

@compare[
@racketmod0[#:file
@tt{@;{good}好的}
racket
...
(define cop
  current-output-port)

(code:comment #, @t{String OPort -> Void})
(define (send msg op)
  (parameterize ((cop op))
    (display msg))
  (record msg))
]
@; -----------------------------------------------------------------------------
@racketmod0[#:file
@tt{@;{bad}差的}
racket
...
(define cop
  current-output-port)

(code:comment #, @t{String OPort -> Void})
(define (send msg op)
  (define cp (cop))
  (cop op)
  (display msg)
  (cop cp)
  (record msg))
]
]

@;{As the comparison demonstrates, @racket[parameterize] clearly delimits the
extent of the change, which is an important idea for the reader. In
addition, @racket[parameterize] ensures that your code is more likely to
work with continuations and threads, an important idea for Racket
programmers.}
如比较所示，@racket[parameterize]清楚地界定了变化的范围，这对读取器来说是一个重要的想法。此外，@racket[parameterize]确保代码更可能使用延续和线程，这是Racket程序员的一个重要概念。


@section{@;{Plural}复数}

@;{Avoid plural when naming collections and libraries. Use @racketmodname[racket/contract]
and @racketmodname[data/heap], not @tt{racket/contracts} or @tt{data/heaps}.}
命名集合和库时避免使用复数形式。使用@racketmodname[racket/contract]和@racketmodname[data/heap]，而不是@tt{racket/contracts}或@tt{data/heaps}。
